package com.edu.framework.websocket.service;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson2.JSONObject;
import com.edu.framework.websocket.WebSocketHolder;
import com.edu.framework.websocket.message.base.Message;
import com.edu.framework.websocket.message.base.Response;
import com.edu.framework.websocket.message.card.CardInfoVO;
import com.edu.framework.websocket.message.card.CardListRequest;
import com.edu.framework.websocket.message.card.CardRequest;
import com.edu.framework.websocket.message.card.CardVO;
import com.edu.framework.websocket.message.card.*;
import com.edu.system.domain.TCard;
import com.edu.system.domain.TChargeRecord;
import com.edu.system.domain.TClass;
import com.edu.system.service.ITCardService;
import com.edu.system.service.ITChargeRecordService;
import com.edu.system.service.ITClassService;
import com.edu.system.service.ITTerminalService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.WebSocketSession;

/**
 * @program: xxxx
 * @description:
 * @author: zlh
 * @create: 2024-01-06 18:20
 **/
@Slf4j
@Component
public class CardService {

  @Resource
  private ITCardService itCardService;
  @Resource
  private ITClassService itClassService;
  @Resource
  private ITTerminalService itTerminalService;

  @Resource
  private ITChargeRecordService chargeRecordService;


  public Response<CardVO> getCardInfo(String cardNo){
    TCard card = new TCard();
    card.setCardNo(cardNo);
    card.setIsDeleted(0);
    card.setStatus(0);
    List<TCard> list = itCardService.selectTCardList(card);

    if (CollectionUtils.isEmpty(list)){
      return Response.ok(CardRequest.TYPE, CardRequest.RESP_KEY, null);
    }
    String className = Optional.ofNullable(list.get(0).getClassId())
        .map(a -> itClassService.selectTClassById(a))
        .map(TClass::getName)
        .orElseGet(() -> "");

    CardVO cardVO = Optional.of(list.get(0))
        .map(a -> CardVO.builder()
            .owner(a.getOwner())
            .className(className)
            .balance(a.getBalance().toString())
            .build())
        .orElseGet(() -> null);
    return Response.ok(CardRequest.TYPE, CardRequest.RESP_KEY, cardVO);
  }

  /**
   *
   * @param vo
   * @param isWriteOff 是否是核销
   * @return
   */
  @Transactional
  public Response<CardInfoVO> charge(TapVO vo, boolean isWriteOff, Integer isOnline){
    // 处理离线刷卡流水
    if (StrUtil.isNotBlank(vo.getOfflineRecords())){
      log.info("有离线刷卡流水记录: {}", vo.getOfflineRecords());
      this.diffCharge(vo.getOfflineRecords());
    }

    String cardId = vo.getCardId();
    String fee = vo.getFee();
    TCard card = this.itCardService.findCardByNo(cardId);
    CardInfoVO infoVO = new CardInfoVO();
    infoVO.setCardId(cardId);
    infoVO.setChargeTime(vo.getChargeTime());
    if(null==card){
      String message = "卡不存在："+cardId;
      infoVO.setFailedMessage(message);
      return Response.ok(TapCardRequest.TYPE, TapCardRequest.RESP_KEY,infoVO);
    }
    Float balance = card.getBalance();
    Float fAmount = Float.valueOf(fee);
    infoVO.setClassName(vo.getClassName());
    infoVO.setName(card.getOwner());

    // 消费时，如果已存在消费记录，则直接返回
    if (this.checkIsExist(vo)){
      infoVO.setBalance(balance);
      return Response.ok(TapCardRequest.TYPE, TapCardRequest.RESP_KEY,infoVO);
    }

    Float after = balance-fAmount;
    // 余额不足
    if(!isWriteOff && after<0){
      infoVO.setBalance(balance);
      String message = "余额不足，余额为: "+ balance + "元";
      infoVO.setFailedMessage(message);
      return Response.ok(TapCardRequest.TYPE, TapCardRequest.RESP_KEY,infoVO);
    }
    card.setBalance(after);
    this.itCardService.updateTCard(card);
    this.chargeRecordService.insertTChargeRecord(createRecord(vo, card, isOnline));
    infoVO.setBalance(after);


    return Response.ok(TapCardRequest.TYPE, TapCardRequest.RESP_KEY,infoVO);
  }

  private TChargeRecord createRecord(TapVO vo,TCard card, Integer isOnline){
    String cardId = vo.getCardId();
    Long chargeTime = vo.getChargeTime();
    TChargeRecord record = new TChargeRecord();
    record.setOwnerName(card.getOwner());
    record.setCardNo(cardId);
    record.setOwnerId(card.getOwnerId());

    record.setBillNo(MD5.create().digestHex16(card + chargeTime.toString()));
    record.setAmount(Float.valueOf(vo.getFee()));
    record.setChargeType(vo.getMealType());
    record.setBalance(card.getBalance());
    record.setCanteenId(Long.parseLong(vo.getCanteenId()));
    record.setCardType(card.getType());
    record.setClassId(card.getClassId());
    record.setChargeTime(new Date((chargeTime/1000)*1000));
    record.setCreateTime(new Date());
    record.setIsOnline(isOnline.toString());
    return record;
  }


  public Response<List<CardInfoVO>> cardList(List<Long> ids){

    // 无ids，则全量
    List<TCard> list = Optional.ofNullable(ids)
        .filter(CollectionUtils::isNotEmpty)
        .map(a -> itCardService.selectCardByIds(a))
        .orElseGet(() -> itCardService.selectTCardList(new TCard()));

    List<CardInfoVO> collect = list
        .stream()
        .map(a -> {
          int v = 1;
          if (!a.isValidCard()){
            v = 0;
          }
          return CardInfoVO.builder()
              .cardId(a.getCardNo())
              .name(a.getOwner())
              .className(a.getClassName())
              .balance(a.getBalance())
              .valid(v)
              .build();
        })
        .collect(Collectors.toList());

    return Response.ok(CardListRequest.TYPE, CardListRequest.RESP_KEY, collect);
  }

  /**
   * 下发全量卡信息
   * @return
   */
  public void pushCardList(List<Long> ids){

    Response<List<CardInfoVO>> listResponse = this.cardList(ids);

    Function<String, Message> fc = code -> {
      log.info("下发卡，机器:{}", code);
//      Response<List<CardInfoVO>> listResponse = this.cardList(ids);
      log.info("下发卡, 数据：{}", JSONObject.toJSONString(listResponse));
      return listResponse;
    };
    Consumer<Message> invoke = message -> {};
    try {
      WebSocketHolder.broadcast(fc, invoke);
    } catch (Exception e){
      log.error("主动下发失败 卡列表", e);
    }
  }


  /**
   * 对账消费记录
   *
   * @param text
   * @return
   */
  public Response<String> diffCharge(String text){
    log.info("刷卡流水记录：" + text);

    String[] arr = text.split("\n");
    List<TapVO> tapList = new ArrayList<>();
    for(int i=0;i<arr.length;i++){
      if (StrUtil.isBlank(arr[i]) || !arr[i].contains(" ")){
        log.warn("接收的对账记录 存在不合法数据：" + arr[i]);
        continue;
      }
      String[] s = arr[i].split(" ");

      // todo 检查字段是否对应上
      TapVO tap = new TapVO();
      tap.setCardId(s[0]);
      tap.setCanteenId(s[3]);
      tap.setMealType(s[4]);
      tap.setFee(s[5]);
      tap.setChargeTime(Long.parseLong(s[6]));
      tapList.add(tap);

      // 0离线 1在线
      if (s[7].equals("1")){
        continue;
      }

      if (this.checkIsExist(tap)){
        continue;
      }

      // 核销
      Response<CardInfoVO> charge = this.charge(tap, true, 0);
      if (StrUtil.isNotBlank(charge.getData().get(TapCardRequest.RESP_KEY).getFailedMessage())){
        log.error("接收的对账记录 核销失败，记录：{}", arr[i]);
        continue;
      }
      log.info("接收的对账记录 核销成功，记录：{}", arr[i]);
    }

    return Response.ok(TapRecordsRequest.TYPE, TapRecordsRequest.RESP_KEY, "success");
  }

  private boolean checkIsExist(TapVO tap) {
    TChargeRecord r = new TChargeRecord();
    r.setCardNo(tap.getCardId());
    r.setChargeTime(new Date((tap.getChargeTime()/1000)*1000));
    List<TChargeRecord> record = chargeRecordService.selectTChargeRecordList(r);
    return CollectionUtils.isNotEmpty(record);
  }

  /**
   * 主动发起拉消费记录
   */
  public void pullChargeRecord(){
    Map<String, WebSocketSession> sessions = WebSocketHolder.getSessions();

    sessions.forEach((c,v)->{
      try {
        Response<String> ok = Response.ok(TapRecordsRequest.TYPE, TapRecordsRequest.RESP_KEY, "");

        Function<String, Message> fc = code -> ok;
        Consumer<Message> invoke = message -> {};
        WebSocketHolder.broadcastForCode(Collections.singletonList(c),fc,invoke);

        // 休眠几秒再去申请其他机器
        Thread.sleep(10000);
      } catch (Exception e) {
        log.error("主动下发失败 申请推送消费记录", e);
      }
    });

  }




}
